package stc.skymobi.netty.handler.codec.memcached;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import stc.skymobi.bean.bytebean.codec.StringCodec;
import stc.skymobi.bean.bytebean.core.ByteFieldDesc;
import stc.skymobi.bean.bytebean.core.DecContext;
import stc.skymobi.bean.bytebean.core.DecResult;
import stc.skymobi.util.NumberCodec;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

public class MemcachedStringCodec extends StringCodec {
    private static final Logger logger = LoggerFactory.getLogger(MemcachedStringCodec.class);

    @Override
    public DecResult decode(DecContext ctx) {
        byte[] bytes = ctx.getDecBytes();
        ByteFieldDesc desc = ctx.getFieldDesc();
        if (null == desc) {
            throw new RuntimeException("StringCodec: ByteFieldDesc is null");
        }

        int length = desc.getFixedLength();

        boolean needFilter = false;
        if (length > 0) needFilter = true;

        if (length < 0) {
            length = desc.getStringLengthInBytes(ctx.getDecOwner());
        }

        //added for memcached
        if (length < 0 && "value".equalsIgnoreCase(desc.getField().getName())) {
            Class clazz = ctx.getDecOwner().getClass();
            try {
                length = (Integer) clazz.getMethod("getTotalBodyLength").invoke(ctx.getDecOwner())
                        - (Short) clazz.getMethod("getKeyLength").invoke(ctx.getDecOwner())
                        - (Byte) clazz.getMethod("getExtrasLength").invoke(ctx.getDecOwner());
            } catch (IllegalAccessException e) {
                logger.warn("", e);
            } catch (NoSuchMethodException e) {
                logger.warn("", e);
            } catch (InvocationTargetException e) {
                logger.warn("", e);
            }
        }

        if (length < 0) {
            throw new RuntimeException("StringCodec: length < 0");
        }
        NumberCodec numberCodec = ctx.getNumberCodec();

        String charset = numberCodec.convertCharset(desc.getCharset());

        String ret = null;

        if (length > bytes.length) {
            String errmsg = "StringCodec: not enough bytes for decode, need [" + length
                    + "], actually [" + bytes.length + "].";
            if (null != ctx.getField()) {
                errmsg += "/ cause field is [" + ctx.getField() + "]";
            }
            logger.error(errmsg);
            throw new RuntimeException(errmsg);
            //return makeDecResult( ret, bytes);
        }

        if (length > 0) {
            try {
                byte[] values = Arrays.copyOfRange(bytes, 0, length);

                if (needFilter && charset.startsWith("UTF-16")) {
                    int len = values.length;
                    for (; len > 0; ) {
                        if ((values[len - 1] & 0xFF) == 0) {
                            len -= 2;
                        } else break;
                    }
                    values = Arrays.copyOfRange(values, 0, len);
                }

                ret = new String(values, charset);
            } catch (UnsupportedEncodingException e) {
                logger.error("StringCodec", e);
            }
        }

        return new DecResult(ret, Arrays.copyOfRange(bytes, length, bytes.length));
    }
}
